package StudyCode.summary.utf;

import java.io.InputStream;

/**
 * 版权所有 科技与人文(www.tah1986.com)
 */
public class ReadsStringUTF8EncodingFromDataStreamReturns {
	public static String ReadUtf8ToString(InputStream stream)
			throws java.io.IOException {
		return ReadUtf8ToString(stream, -1, true);
	}

	public static String ReadUtf8ToString(InputStream stream,
	                                      int bytesCount, boolean replace) throws java.io.IOException {
		StringBuilder builder = new StringBuilder();
		ReadsStringUTF8EncodingFromDataStreamReturns.ReadUtf8(stream, bytesCount, builder, replace);
		return builder.toString();
	}

	public static int ReadUtf8(InputStream stream, int bytesCount,
	                           StringBuilder builder, boolean replace)
			throws java.io.IOException {
		if (stream == null) {
			throw new NullPointerException("stream");
		}
		if (builder == null) {
			throw new NullPointerException("builder");
		}
		int cp = 0;
		int bytesSeen = 0;
		int bytesNeeded = 0;
		int lower = 0x80;
		int upper = 0xbf;
		int pointer = 0;
		while (pointer < bytesCount || bytesCount < 0) {
			int b = stream.read();
			if (b < 0) {
				if (bytesNeeded != 0) {
					bytesNeeded = 0;
					if (replace) {
						builder.append((char) 0xfffd);
						if (bytesCount >= 0) {
							return -2;
						}
						break;
					}
					return -1;
				}
				if (bytesCount >= 0) {
					return -2;
				}
				break;
			}
			if (bytesCount > 0) {
				++pointer;
			}
			if (bytesNeeded == 0) {
				if ((b & 0x7f) == b) {
					builder.append((char) b);
				} else if (b >= 0xc2 && b <= 0xdf) {
					bytesNeeded = 1;
					cp = (b - 0xc0) << 6;
				} else if (b >= 0xe0 && b <= 0xef) {
					lower = (b == 0xe0) ? 0xa0 : 0x80;
					upper = (b == 0xed) ? 0x9f : 0xbf;
					bytesNeeded = 2;
					cp = (b - 0xe0) << 12;
				} else if (b >= 0xf0 && b <= 0xf4) {
					lower = (b == 0xf0) ? 0x90 : 0x80;
					upper = (b == 0xf4) ? 0x8f : 0xbf;
					bytesNeeded = 3;
					cp = (b - 0xf0) << 18;
				} else {
					if (replace) {
						builder.append((char) 0xfffd);
					} else {
						return -1;
					}
				}
				continue;
			}
			if (b < lower || b > upper) {
				cp = bytesNeeded = bytesSeen = 0;
				lower = 0x80;
				upper = 0xbf;
				if (replace) {
					builder.append((char) 0xfffd);
					if (b < 0x80) {
						builder.append((char) b);
					} else if (b >= 0xc2 && b <= 0xdf) {
						bytesNeeded = 1;
						cp = (b - 0xc0) << 6;
					} else if (b >= 0xe0 && b <= 0xef) {
						lower = (b == 0xe0) ? 0xa0 : 0x80;
						upper = (b == 0xed) ? 0x9f : 0xbf;
						bytesNeeded = 2;
						cp = (b - 0xe0) << 12;
					} else if (b >= 0xf0 && b <= 0xf4) {
						lower = (b == 0xf0) ? 0x90 : 0x80;
						upper = (b == 0xf4) ? 0x8f : 0xbf;
						bytesNeeded = 3;
						cp = (b - 0xf0) << 18;
					} else {
						builder.append((char) 0xfffd);
					}
					continue;
				}
				return -1;
			} else {
				lower = 0x80;
				upper = 0xbf;
				++bytesSeen;
				cp += (b - 0x80) << (6 * (bytesNeeded - bytesSeen));
				if (bytesSeen != bytesNeeded) {
					continue;
				}
				int ret = cp;
				cp = 0;
				bytesSeen = 0;
				bytesNeeded = 0;
				if (ret <= 0xffff) {
					builder.append((char) ret);
				} else {
					int ch = ret - 0x10000;
					int lead = (ch / 0x400) + 0xd800;
					int trail = (ch & 0x3ff) + 0xdc00;
					builder.append((char) lead);
					builder.append((char) trail);
				}
			}
		}
		if (bytesNeeded != 0) {
			if (replace) {
				builder.append((char) 0xfffd);
			} else {
				return -1;
			}
		}
		return 0;
	}
}
